package lvm;

use File::Basename;
use Data::Dumper;
use Sys::Hostname;
use mnttab;
use strict;

my $df_portable=1;
eval "use Filesys::DfPortable";
if($@) {
	$df_portable=0;
}

#########################################################################
# This module provides a series of routines to interface with the lvm	#
# subsystem - either the original lvm or lvm2/device mapper.		#
# Modified to work on HP-UX also with VxVm.				#
#########################################################################

sub msg {
	if(defined(&::msg)) {
		::msg(@_);
	}
}

sub warning {
	if(defined(&::warning)) {
		::warning(@_);
	}
}

sub error {
	if(defined(&::error)) {
		::error(@_);
	}
}
sub new {
my $proto=shift;
my %args=@_;
my $class=ref($proto) || $proto;
my $self={};
	
	$self->{HAS_LVM}=0;
	$self->{HAS_VXVM}=0;
	$self->{HAS_SOLVM}=0;
	$self->{HAS_ZFSVM}=0;
	$self->{HAS_AIXVM}=0;
	$self->{VXVM_LIST}=[];
	$self->{LVM_LIST}=[];
	$self->{SOLVM_LIST}=[];
	$self->{ZFSVM_LIST}=[];
	$self->{AIXVM_LIST}=[];
	$self->{OS}=ucfirst($^O);
	if($^O =~ /linux/i) {
	#################################################################
	# The first thing we do is to ascertain whether we have		#
	# /proc/lvm directory - or if not if /proc/misc contains a 	#
	# device-mapper entry.						#
	#################################################################
		if(-d "/proc/lvm") {
			$self->{HAS_LVM}=1;
		}
		if(-f "/proc/misc") {
			my ($fd,$cline);
			open($fd,"/proc/misc");
			while($cline=<$fd>) {
				$self->{HAS_LVM}=1 if $cline =~ /device-mapper/;
			}
			close($fd);
		}
		if(-x "/usr/sbin/vxdg" || -x "/sbin/vxdg") {
			$self->{HAS_VXVM}=1;
		}
	} elsif($^O eq "aix" && -x "/usr/sbin/lsvg") {
		$self->{HAS_AIXVM}=1;
	} elsif ($^O eq "solaris" && -x "/sbin/metastat") {
		$self->{HAS_SOLVM}=1;
	}
	$self->{HAS_ZFSVM}=1 if (-x "/sbin/zfs" || -x "/usr/sbin/zfs");
	$self->{HAS_VXVM}=1 if -x "/usr/sbin/vxdg";
	$self->{HAS_LVM}=1 if -x "/usr/sbin/vgcreate";
	if($self->{HAS_ZFSVM}+$self->{HAS_LVM}+$self->{HAS_AIXVM}+$self->{HAS_VXVM}+$self->{HAS_SOLVM} ==0) {
		return undef;
	}
	my @W=("/usr/sbin","/sbin","/usr/local/sbin");
	if($self->{HAS_LVM}) {
		for (@W) {
			if(-x "$_/vgcreate") {
				$self->{LVM_PATH}=$_;
				last;
			}
		}
	}
	if($self->{HAS_AIXVM}) {
		for (@W) {
			if(-x "$_/lsvg") {
				$self->{AIXVM_PATH}=$_;
				last;
			}
		}
	}
	if($self->{HAS_VXVM}) {
		for (@W) {
			if(-x "$_/vxdg") {
				$self->{VXVM_PATH}=$_;
				last;
			}
		}
	}
	if($self->{HAS_SOLVM}) {
		for (@W) {
			if(-x "$_/metaset") {
				$self->{SOLVM_PATH}=$_;
				last;
			}
		}
	}
	if($self->{HAS_ZFSVM}) {
		for (@W) {
			if(-x "$_/zfs") {
				$self->{ZFSVM_PATH}=$_;
				last;
			}
		}
	}

	_load_lvm_details($self) if $self->{HAS_LVM};
	_load_vxvm_details($self) if $self->{HAS_VXVM};
	_load_solvm_details($self) if $self->{HAS_SOLVM};
	_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	_load_aixvm_details($self) if $self->{HAS_AIXVM};
	bless($self,$class);
	return $self;
}

sub _load_aixvm_details($) {
my $self=shift;
my @L=();
my ($fd,$cline,$cvg,$clv);

	local $SIG{CHLD}='DEFAULT';
	msg("Refreshing AIX LVM configuration.");
	return if ! -x "$self->{AIXVM_PATH}/lsvg";
	open($fd,"$self->{AIXVM_PATH}/lsvg 2>/dev/null|");
	if(!defined($fd)) {
		$self->{AIXVM_LIST}=[];
		my $ckey;
		for $ckey (keys %$self) {
			if($ckey =~ /^AIXVM_LVLIST_/) {
				delete $self->{$ckey};
			}
		}
		return;
	}
	my @vglist=();
	push @vglist, map {chomp $_; $_} <$fd>;
	close($fd);

	# Now we need to get the detailts of each volume group

	my %_vginfo=();
	for my $cvg (@vglist) {
		my $r=`$self->{AIXVM_PATH}/lsvg $cvg 2>/dev/null`;
		if(!defined($r) || length($r)<100) {
			next;
		}
		my $open=0; my $ppsize=0; my $ppused=0;
		my $ppfree=0; my $pptotal=0; my $autoon=0;
		my $status=undef;
		my @F=split(/\n/,$r);
		for my $cline (@F) {
			if($cline =~ /PP SIZE:\s+(\d+)/) {
				$ppsize=int($1);
			}
			if($cline =~ /TOTAL PPs:\s+(\d+)/i) {
				$pptotal=int($1);
			}
			if($cline =~ /FREE PPs:\s+(\d+)/i) {
				$ppfree=int($1);
			}
			if($cline =~ /USED PPs:\s+(\d+)/i) {
				$ppused=int($1);
			}
			if($cline =~ /OPEN LV:\s+(\d+)/i) {
				$open=int($1);
			}
			if($cline =~ /VG STATE:\s+(\w+)\s+/i) {
				$status=$1;
			}
			if($cline =~ /AUTO ON:\s+(\w+)/i) {
				$autoon=1 if $1 eq "yes";
			}
		}
		$self->{"AIX_VGDETAILS_$cvg"}={
			PPSIZE	=> $ppsize,
			PPUSED	=> $ppused,
			PPFREE	=> $ppfree,
			PPTOTAL	=> $pptotal,
			STATUS	=> $status,
			AUTOON	=> $autoon,
			OPENLVS	=> $open,
		};

		#########################################################
		# We have the vg defails, now get the details of each 	#
		# of the logical volumes...				#
		#########################################################
		$r=`$self->{AIXVM_PATH}/lsvg -l $cvg 2>/dev/null`;
		@F=split(/\n/,$r);
		for my $cline (@F[2 .. $#F]) {
			my @dets=split(/\s+/,$cline);
			if($dets[1] =~ /^jfs[2]{0,1}$/) {
				my $cvol=$dets[0];
				my $csize=$dets[2] * $ppsize * 1024;
				my $copen=0;
				$copen=1 if $dets[5] =~ /^open/;
				$dets[5] =~ /^.*\/(.*)/;
				my $cstatus=$1;

				if(!exists($self->{"AIX_LVLIST_$cvg"})) {
					$self->{"AIX_LVLIST_$cvg"}={};
				}

				if(! grep {$_ eq $cvol} @{$self->{AIXVM_LIST}}) {
					push @{$self->{AIXVM_LIST}},$cvol;
				}
				$self->{"AIX_LVLIST_$cvg"}->{$cvol}={
					SIZE	=> $csize,
					NAME	=> $cvol,
					OPEN	=> $copen,
					STATUS	=> $cstatus,
				};
			}
		}
	}
}

# ZFS is mainly used for solaris, though we do not rely on that
# being the case - only that the output of the utilities is the
# same across all supported operating systems.

sub _load_zfsvm_details($) {
my $self=shift;
my @L=();
my ($fd,$cline,$cvg,$clv);

	local $SIG{CHLD}='DEFAULT';
	msg("Refreshing ZFS configuration.");
	return if ! -x "$self->{ZFSVM_PATH}/zfs";
	open($fd,"$self->{ZFSVM_PATH}/zfs list -H 2>/dev/null|");
	if(!defined($fd)) {
		$self->{ZFSVM_LIST}=[];
		my $ckey;
		for $ckey (keys %$self) {
			if($ckey =~ /^ZFSVM_LVLIST_/) {
				delete $self->{$ckey};
			}
		}
		return;
	}
	# The output of the zfs list is as follows [though without the
	# header since -H was specified]:
	# NAME                           USED  AVAIL  REFER  MOUNTPOINT
	# rpool/export                  52.0M  8.28G    19K  /export
	# rpool/export/home             51.9M  8.28G  51.9M  /export/home
	# share1zfs                      120K   984M    18K  legacy
	# share1zfs/data01              18.5K   256M  18.5K  legacy
	#
	# Heece we record size/used/avail - converting the sizes to mb
	# if in Gb...
	my %VGS=();
	while($cline=<$fd>) {
		chomp $cline;
		my ($zname,$zused,$zavail,$zrefer,$zmnt)=split(/\s+/,$cline);
		# ignore the top-level lines
		my @F=split(/\//,$zname);
		if(@F==1) {
			next;
		}
		my ($zpool,$zvol);
		$zpool=$F[0];
		$zvol=join("/",@F[1 .. $#F]);
		my $za=$zavail;
		$za =~ s/[MGT]$//;
		my $csize=$za;
		if($zavail =~ /M$/) {
			$csize*=1024;
		}
		if($zavail =~ /G$/) {
			$csize*=1024*1024;
		}
		if($zavail =~ /T$/) {
			$csize*=1024*1024*1024;
		}
		$self->{"ZFSVM_LVLIST_$zpool"}->{$zvol}={
			SIZE	=> $csize,
			NAME	=> $zvol,
			OPEN	=> -1, # do not know if open
			STATUS	=> "", # look at zpool for this info
		}
	}
	close($fd);
	open($fd,"$self->{ZFSVM_PATH}/zpool list -H 2>/dev/null|");
	while($cline=<$fd>) {
		chomp $cline;
		my @F=split(/\s+/,$cline);
		push @{$self->{ZFSVM_LIST}},$F[0];
		for my $ckey (keys %$self) {
			if($ckey eq "ZFSVM_LVLIST_$F[0]") {
				for my $ckey2 (keys %{$self->{$ckey}}) {
					$self->{$ckey}->{$ckey2}->{STATUS}=$F[5];
				}
			}
		}
	}
	close($fd);
}

sub _load_solvm_details($) {
my $self=shift;
my @L=();
my ($fd,$cline,$cvg,$clv);

	local $SIG{CHLD}='DEFAULT';
	msg("Refreshing MD configuration.");
	return if ! -x "$self->{SOLVM_PATH}/metastat";
	open($fd,"$self->{SOLVM_PATH}/metastat -ac 2>/dev/null|");
	if(!defined($fd)) {
		$self->{SOLVM_LIST}=[];
		my $ckey;
		for $ckey (keys %$self) {
			if($ckey =~ /^SOL_LVLIST_/) {
				delete $self->{$ckey};
			}
		}
		return;
	}
	# The top level volume are on the lines where the left most
	# item has no leading white space.
	while($cline=<$fd>) {
		chomp $cline;
		next if $cline =~ /^\s+/;
		($clv)=($cline =~ /^(\S+)\s+/);
		if(defined($clv)) {
			push @L,$clv;
		}
	}
	close($fd);

	# We have a list of volumes, though now need to get move
	# detailed information about each one...

	open($fd,"/sbin/metastat -a 2>/dev/null|");
	return if !defined($fd);
	my ($ctype,$csize,$cvol,$cstatus,$cdev);
	my %vols=();
	my $cds;
	while($cline=<$fd>) {
		chomp $cline;
		if($cline =~ /^(\S+): /) {
			$cvol=$1;
			if($cvol =~ /\//) {
				my ($ds,$vol)=split(/\//,$cvol);
				if(!exists($self->{"SOL_LVLIST_$ds"})) {
					$self->{"SOL_LVLIST_$ds"}={};
				}
				$cvol = $vol;
				if(! grep {$_ eq $ds} @{$self->{SOLVM_LIST}}) {
					push @{$self->{SOLVM_LIST}},$ds;
				}
				$cds=$ds;
			} else {
				$cds=undef;
			}
			($ctype)=($cline =~ /^\S+: (.+)/);
		}
		if($cline =~ /^\s+Device:\s+(.*)/) {
			$cdev=$1;
		}
		if($cline =~ /^\s+Size: (\d+) /) {
			$csize=$1;
			$vols{$cvol}={SIZE => int($csize)/2, STATUS => $cstatus};
			if(defined($cds)) {
				$self->{"SOL_LVLIST_$cds"}->{$cvol}={
					SIZE	=> $csize/2,
					NAME	=> $cvol,
					TYPE	=> $ctype,
					DEV	=> $cdev,
					OPEN	=> -1,
					STATUS	=> $cstatus,
				};
			}
		}
		if($cline =~ /^\s+State:\s+(\S+)/) {
			$cstatus=$1;
		}
	}
	close($fd);
}

sub _load_lvm_details($) {
my $self=shift;
my @L=();
my ($fd,$cline,$cvg,$clv);

	local $SIG{CHLD}='DEFAULT';
	msg("Refreshing LVM configuration.");
	my $p=$self->{LVM_PATH};
	return if ! -x "$p/vgdisplay";	# Ignore request if no LVM
	open($fd,"$p/vgdisplay 2>/dev/null|");
	if(!defined($fd)) {
		$self->{LVM_LIST}=[];
		my $ckey;
		for $ckey (keys %$self) {
			if($ckey =~ /^LVM_LVLIST_/) {
				delete $self->{$ckey};
			}
		}
		return;
	}
	my @EX=();
	my $cstatus;
	my $lvg;
	while($cline=<$fd>) {
		chomp $cline;
		($cvg)=($cline =~ /^\s*VG Name\s+(\w+)$/);
		if(defined($cvg)) {
			$lvg=$cvg;
			push @L,$cvg;
		}
		($cstatus)=($cline =~ /^\s*VG Status\s+(.*)$/);
		if(defined($cstatus) && $cstatus =~ /exported/) {
			push @EX,$lvg;
		}
	}
	# If we have exported volume groups, ignore them from list
	if(@EX) {
		my @NL=();
		for my $x (@L) {
			if(! grep {$x eq $_} @EX) {
				push @NL,$x;
			}
		}
		@L=@NL;
	}
	close($fd);
	$self->{LVM_LIST}=[@L];
	my @L2=();
	my $h;
	my $vgname;
	for $vgname (@L) {
		$h={};
		$h->{name}=$vgname;
		open($fd,"$p/vgdisplay -v $vgname 2>/dev/null|");
		return undef if !defined($fd);
		my $clv=undef;
		my $clvh={};
		while($cline=<$fd>) {
			my $x;
			chomp $cline;
			if(defined($clv) && defined($clvh) && 4==scalar(keys(%$clvh))) {
				$self->{"LVM_LVLIST_$vgname"}->{$clv}=$clvh;
				$clvh=undef;
			}
			if($cline =~ /LV Name/) {
				($clv)=($cline =~ /^\s*LV Name\s+(.*)/);
				if(defined($clv)) {
					$clv=basename($clv);
				}
				$clvh={};
				$clvh->{NAME}=$clv;
			}
			if($cline =~ /LV Size/) {
				($x)=($cline =~ /^\s*LV Size\s+([0-9\.]+)\s+/);
				if(defined($x)) {
					$clvh->{SIZE}=sprintf("%d",$x*512);
					# If line contains GB then multiply again!
					$clvh->{SIZE}*=2 if $cline =~ /(MB|Mbytes)/;
					$clvh->{SIZE}*=2048 if $cline =~ /GB/;
				}
			}
			if($cline =~ /# open/) {
				($x)=($cline =~ /^\s*# open\s+([0-9]+)$/);
				if(defined($x)) {
					$clvh->{OPEN}=$x;
				}
			}
			if($cline =~ /LV Status/) {
				($x)=($cline =~ /^\s*LV Status\s+(\w+)/);
				if(defined($x)) {
					$clvh->{STATUS}=$x;
				}
			}
		}
		close($fd);
	}
}

sub _load_vxvm_details($) {
my $self=shift;
my @L=();
my ($fd,$cline,$cvg,$clv);

	local $SIG{CHLD}='DEFAULT';
	msg("Refreshing VXVM configuration.");
	my $p=$self->{VXVM_PATH};
	return if ! -x "$p/vxprint"; # ignore request if no VxVM
	open($fd,"$p/vxprint -a|");
	if(!defined($fd)) {
		$self->{VXVM_LIST}=[];
		my $ckey;
		for $ckey (keys %$self) {
			if($ckey =~ /^VXVM_LVLIST_/) {
				delete $self->{$ckey};
			}
		}
		return;
	}
	my @lines=<$fd>; close($fd);
	for (@lines) {
		my @F=split;
		next if ! @F;
		push @L,$F[1] if $F[0] eq "dg";
	}
	$self->{VXVM_LIST}=[@L];

	# get NAME,SIZE,STATUS,OPEN

	for $cline (@lines) {
		chomp $cline;
		my @F=split(/\s+/,$cline);
		next if $#F<5;
		if($F[0] eq "dg") {
			$cvg=$F[1];
			next;
		}
		next if $F[0] ne "vol";
		$clv=$F[1];
		my $clvh={};
		$clvh->{NAME}=$clv;
		my $x;
		($x)=grep {/^len=/} @F;
		if(defined($x)) {
			$x =~ s/^.*=//;
			$clvh->{SIZE}=int($x);
		}
		($x)=grep {/^state=/} @F;
		if(defined($x)) {
			$x =~ s/^.*=["]{0,1}//;
			$clvh->{STATUS}=$x;
		}
		($x)=grep {/^devopen=/} @F;
		if(defined($x)) {
			$x =~ s/^.*=//;
			if($x eq "off") {
				$clvh->{OPEN}="CLOSED";
			} else {
				$clvh->{OPEN}="OPEN";
			}
		}
		$self->{"VXVM_LVLIST_$cvg"}->{$clv}=$clvh;
	}

	#########################################################################
	# We just empty the list of deported disk groups - and only get them	#
	# when explicity requested via the vg_list method.			#
	#########################################################################
	$self->{VXVM_DEPORTED_LIST}=[];
	msg("Refreshed VXVM configuration successfully.");
}

sub type {
my $self=shift;
my @R=();

	push @R,"LVM" if $self->{HAS_LVM};
	push @R,"VXVM" if $self->{HAS_VXVM};
	push @R,"SOLVM" if $self->{HAS_SOLVM};
	push @R,"AIXVM" if $self->{HAS_AIXVM};
	push @R,"ZFSVM" if $self->{HAS_ZFSVM};
	return join(",",@R);
}

sub describe_type {
my $self=shift;
my $type=shift;
my %types=(
	LVM	=> "Logical Volume Manager",
	VXVM	=> "Veritas Volume Manager",
	SOLVM	=> "Solaris Meta Volume Manager",
	AIXVM	=> "AIX Volume Manager",
	ZFSVM	=> "ZFS Volume Manager",
	);

	$type="??not_specified??" if !defined($type);
	if(exists($types{$type})) {
		return $types{$type};
	}
	return "$type - not recognised";
}

sub vg_list {
my $self=shift;
my %args=@_;

	local $SIG{CHLD}='DEFAULT';
	if((exists($args{UPTODATE}) && $args{UPTODATE} ==1)) {
		_load_lvm_details($self) if $self->{HAS_LVM};
		_load_vxvm_details($self) if $self->{HAS_VXVM};
		_load_solvm_details($self) if $self->{HAS_SOLVM};
		_load_aixvm_details($self) if $self->{HAS_AIXVM};
		_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	}
	my @R=();
	push @R,"$_ LVM" for (@{$self->{LVM_LIST}});
	push @R,"$_ VXVM" for (@{$self->{VXVM_LIST}});
	push @R,"$_ SOLVM" for (@{$self->{SOLVM_LIST}});
	push @R,"$_ AIXVM" for (@{$self->{AIXVM_LIST}});
	push @R,"$_ ZFSVM" for (@{$self->{ZFSVM_LIST}});
	if($self->{HAS_VXVM} && (exists($args{DEPORTED}) && $args{DEPORTED} ==1)) {
		my $p=$self->{VXVM_PATH};
		# We generate the list here rather than in the normal
		# scan to save time in most situations...
		my $fd;
		open($fd,"$p/vxdisk -o alldgs list 2>/dev/null|");
		$self->{VXVM_DEPORTED_LIST}=[];
		if(defined($fd)) {
			my @lines=<$fd>; close($fd);
			my %_haddg=();
			for (@lines) {
				my @F=split;
				next if ! @F;
				next if $F[3] !~ /^\(.*\)$/;
				my $dg=$F[3];
				$dg=~ s/\(//;
				$dg=~ s/\)//;
				next if exists($_haddg{$dg});
				$_haddg{$dg}=1;
				push @{$self->{VXVM_DEPORTED_LIST}},$dg;
			}
		}
		push @R,"$_ VXVM" for (@{$self->{VXVM_DEPORTED_LIST}});
	}
	return (@R);
}

# When returning a list of logical volumes we use the name of the 
# volume group.Having the same named volume group across differnet
# volume managers on the same machine is not supported.

sub lv_list {
my $self=shift;
my %args=@_;

	if(!exists($args{VGNAME})) {
		error("Missing mandatory 'VGNAME' argument.");
		return undef;
	}
	my $vgname=$args{VGNAME};
	if( (exists($args{UPTODATE}) && $args{UPTODATE} ==1)) {
		_load_lvm_details($self) if $self->{HAS_LVM};
		_load_vxvm_details($self) if $self->{HAS_VXVM};
		_load_solvm_details($self) if $self->{HAS_SOLVM};
		_load_aixvm_details($self) if $self->{HAS_AIXVM};
		_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	}

	#################################################################
	# Use the information in the LVM_LVLIST_vgname and		#
	# VXVM_LVLIST_dgname hash to return a list of lvnames.		#
	#################################################################

	if(exists($self->{"SOL_LVLIST_$vgname"})) {
		return (keys(%{$self->{"SOL_LVLIST_$vgname"}}));
	}
	if(exists($self->{"LVM_LVLIST_$vgname"})) {
		return (keys(%{$self->{"LVM_LVLIST_$vgname"}}));
	}
	if(exists($self->{"VXVM_LVLIST_$vgname"})) {
		return (keys(%{$self->{"VXVM_LVLIST_$vgname"}}));
	}
	if(exists($self->{"AIX_LVLIST_$vgname"})) {
		return (keys(%{$self->{"AIX_LVLIST_$vgname"}}));
	}
	if(exists($self->{"ZFSVM_LVLIST_$vgname"})) {
		return (keys(%{$self->{"ZFSVM_LVLIST_$vgname"}}));
	}
	return undef;
}

sub get_lvinfo {
my $self=shift;
my %args=@_;
my ($lvname,$vgname);

	if(!exists($args{VGNAME}) || !exists($args{LVNAME})) {
		error("Missing mandatory 'VGNAME'/'LVNAME' argument(s).");
		return undef;
	}
	$vgname=$args{VGNAME};
	$lvname=$args{LVNAME};
	if( (exists($args{UPTODATE}) && $args{UPTODATE} ==1)) {
		_load_lvm_details($self) if $self->{HAS_LVM};
		_load_vxvm_details($self) if $self->{HAS_VXVM};
		_load_solvm_details($self) if $self->{HAS_SOLVM};
		_load_aixvm_details($self) if $self->{HAS_AIXVM};
		_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	}
	my $k;
	if(exists($self->{"AIX_LVLIST_$vgname"})) {
		$k="AIX_LVLIST_$vgname";
	}
	if(exists($self->{"SOL_LVLIST_$vgname"})) {
		$k="SOL_LVLIST_$vgname";
	}
	if(exists($self->{"ZFSVM_LVLIST_$vgname"})) {
		$k="ZFSVM_LVLIST_$vgname";
	}
	if(exists($self->{"LVM_LVLIST_$vgname"})) {
		$k="LVM_LVLIST_$vgname";
	}
	if(exists($self->{"VXVM_LVLIST_$vgname"})) {
		$k="VXVM_LVLIST_$vgname";
	}
	return(undef) if ! defined($k);
	if(!exists($self->{$k}->{$lvname})) {
		error("No information for '$lvname' in '$k'!");
		return undef;
	}
	return ($self->{$k}->{$lvname}->{STATUS},
		$self->{$k}->{$lvname}->{SIZE},
		$self->{$k}->{$lvname}->{OPEN}
		);
}

# Creating a logical volume does require a little bit of work - since
# each volume manager is actually different.
#
# This is especially true for ZFS where we need to indicate that
# the volume is mountpoint type of legacy.
# We also set the size using quota=size .
# We also set the size using quota=size, but not reservation
# So the storage will be classed as 'thinly provisioned'.

sub lvcreate {
my $self=shift;
my %args=@_;
my ($lvname,$vgname,$partition,$size,$r,$cmd,$type);

	local $SIG{CHLD}='DEFAULT';
	if(!exists($args{VGNAME}) || !exists($args{LVNAME}) ||
		!exists($args{SIZE})) {
		return -1;
	}
	$vgname=$args{VGNAME};
	$lvname=$args{LVNAME};
	$type=uc($args{TYPE}) if exists($args{TYPE});
	if(defined($type) && $type ne "AIXVM" && $type ne "SOLVM" && $type ne "LVM" && $type ne "VXVM" && $type ne "SOLVM" && $type ne "ZFSVM") {
		return (-1,"Volume manager '$type' is not supported.");
	}
	if(defined($type) && $self->{"HAS_$type"}==0) {
		return (-2,"Host does not support '$type'.");
	}
	if(!defined($type) && $self->{HAS_VXVM}) { $type="VXVM"; }
	if(!defined($type) && $self->{HAS_LVM}) { $type="LVM"; }
	if(!defined($type) && $self->{HAS_SOLVM}) { $type="SOLVM"; }
	if(!defined($type) && $self->{HAS_AIXVM}) { $type="AIXVM"; }
	if(!defined($type) && $self->{HAS_ZFSVM}) { $type="ZFSVM"; }
	return (-2,"No known volume manager found.") if !defined($type);
	if($type eq "SOLVM" && !exists($args{PARTITION})) {
		return(-2,"Solaris Volume Manager lv creates must specify a PARTITION to use.");
	}
	$partition=$args{PARTITION} if exists($args{PARTITION});
		
	# Convert the size of megabytes - 
	if($type eq "AIXVM") {
		my $ppsize=$self->{"AIX_VGDETAILS_$vgname"}->{PPSIZE};
		$size=$args{SIZE}/(1024.0*$ppsize);
		if($size != int($size)) {
			return(-3,"Size specified is not a multiple of the volume group PP size [$ppsize Mb]");
		}
	} else {
		$size=$args{SIZE}/1024;
	}
	
	my @r=get_lvinfo($self,@_);
	if(defined($r[0])) {
		return (-2,"Volume '$lvname' already exists in VG/DG '$vgname'.");
	}
	if($size<=0) {
		return (-3,"Logical volume of $args{SIZE} [$size MB] is too small");
	}
	if($type eq "LVM") {
		$cmd="$self->{LVM_PATH}/lvcreate -n $lvname -L $size /dev/$vgname";
	} elsif($type eq "SOLVM") {
		$cmd="/usr/sbin/metainit -s $vgname $lvname -p $partition ${size}m";
	} elsif($type eq "ZFSVM") {
		$cmd="$self->{ZFS_PATH}/zfs create -o quota=${size}m -o mount_point=legacy $vgname/$lvname";
	} elsif($type eq "AIXVM") {
		$cmd="/usr/sbin/mklv -y $lvname $vgname $size";
	} else {
		$cmd="$self->{VXVM_PATH}/vxassist -g $vgname make $lvname ${size}m";
	}
	$r=`$cmd 2>&1`;
	if($?>>8) {
		chomp $r;
		return (-4,"Unable to run '$cmd':\n$r");
	}
	my @_r=get_lvinfo($self,@_,UPTODATE=>1);
	return (0,$_r[1]);
}

sub lvremove {
my $self=shift;
my %args=@_;
my ($lv,$vg,$r,$cmd,$type);

	local $SIG{CHLD}='DEFAULT';
	if((exists($args{UPTODATE}) && $args{UPTODATE} ==1)) {
		_load_lvm_details($self) if $self->{HAS_LVM};
		_load_vxvm_details($self) if $self->{HAS_VXVM};
		_load_solvm_details($self) if $self->{HAS_SOLVM};
		_load_aixvm_details($self) if $self->{HAS_AIXVM};
		_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	}
	if(!exists($args{VGNAME}) || !exists($args{LVNAME})) {
		return (-1,0);
	}
	$vg=$args{VGNAME};
	$lv=$args{LVNAME};
	$type=uc($args{TYPE}) if exists($args{TYPE});
	if(!defined($type)) {
		if(exists($self->{"LVM_LVLIST_$vg"})) {
			$type="LVM";
		}
		if(exists($self->{"AIXVM_LVLIST_$vg"})) {
			$type="AIXVM";
		}
		if(exists($self->{"VXVM_LVLIST_$vg"})) {
			$type="VXVM";
		}
		if(exists($self->{"SOL_LVLIST_$vg"})) {
			$type="SOLVM";
		}
		if(exists($self->{"ZFSVM_LVLIST_$vg"})) {
			$type="ZFSVM";
		}
	}
	if(defined($type) && $type ne "AIXVM" && $type ne "LVM" && $type ne "VXVM" && $type ne "SOLVM" && $type ne "ZFSVM") {
		return (-1,"Unrecogised volume manager!");
	}
	if(defined($type) && $self->{"HAS_$type"}==0) {
		return (-2,"Missing commands for volume manager '$type'.");
	}
	return (-2,"Missing configuration for volume manager '$type'.") if !defined($type);

	# Check that the specified logical volume exists...
	my @lvinfo=get_lvinfo($self,LVNAME=>$lv,VGNAME=>$vg);
	if(!defined($lvinfo[0])) {
		return(-2,"Logical volume '$lv' does not exist in volume group '$vg'.");
	}

	#################################################################
	# We definitely have the lv, so now we attempt to remove it.	#
	#################################################################
	if($type eq "LVM") {
		$cmd="$self->{LVM_PATH}/lvremove -f /dev/$vg/$lv 2>&1";
	} elsif($type eq "AIXVM") {
		$cmd="$self->{AIXVM_PATH}/rmlv /dev/$lv 2>&1";
	} elsif($type eq "VXVM") {
		$cmd="$self->{VXVM_PATH}/vxassist -g $vg remove volume $lv 2>&1";
	} elsif($type eq "SOLVM") {
		$cmd="$self->{SOLVM_PATH}/metaclear -s $vg -p $lv 2>&1";
	} elsif($type eq "ZFSVM") {
		$cmd="$self->{ZFSVM_PATH}/zfs destroy $vg/$lv 2>&1";
	} else {
		return(-2,"Volume manager type '$type' not supported for 'lvremove'.");
	}

	#################################################################
	# We have the necessary command so we now execute it.		#
	#################################################################

	$r=`$cmd`;
	if($?>>8) {
		chomp $r;
		return (-3,$r);
	}
	return(1,undef);
}

#########################################################################
# We assume we are dealing with a soft-partition and abort if not	#
# when attempting to grow a volume on Solaris. On AIX we double check	#
# the new size is a multiple of the current pp size.			#
# When handling zfs we simply use the command:				#
# zfs set quota=NNm vgname/lvname - easy!!!				#
#########################################################################

sub lvresize {
my $self=shift;
my %args=@_;
my ($lvname,$vgname,$size,$r,$cmd,$type);

	if(!exists($args{VGNAME}) || !exists($args{LVNAME}) ||
		!exists($args{SIZE})) {
		return (-1,0);
	}
	$vgname=$args{VGNAME};
	$lvname=$args{LVNAME};
	$type=uc($args{TYPE}) if exists($args{TYPE});
	if(defined($type) && $type ne "AIXVM" && $type ne "LVM" && $type ne "VXVM" && $type ne "SOLVM" && $type ne "ZFSVM") {
		return (-1,"Unrecogised volume manager!");
	}
	if(defined($type) && $self->{"HAS_$type"}==0) {
		return (-2,"Missing commands for volume manager '$type'.");
	}
	if(!defined($type)) {
		if(exists($self->{"LVM_LVLIST_$vgname"})) {
			$type="LVM";
		}
		if(exists($self->{"VXVM_LVLIST_$vgname"})) {
			$type="VXVM";
		}
		if(exists($self->{"ZFSVM_LVLIST_$vgname"})) {
			$type="ZFSVM";
		}
		if(exists($self->{"SOL_LVLIST_$vgname"})) {
			$type="SOLVM";
		}
		if(exists($self->{"AIXVM_LVLIST_$vgname"})) {
			$type="AIXVM";
		}
		return(-1,"Can not find information for volume group '$vgname'.") if !defined($type);
	}

	$size=$args{SIZE};
	my @r=get_lvinfo($self,@_,UPTODATE=>1);
	if(!defined($r[0])) {
		return (-2,"Unable to retrieve info for logical volume.");
	}
	if($size<=0) {
		return (-3,"The size of the logical volume is too small [$size].");
	}

	#################################################################
	# If running AIX double check the new size is suitable for	#
	# the PP size of the volume group in question.			#
	#################################################################

	if($type eq "AIXVM") {
		my $ppsize=$self->{"AIX_VGDETAILS_$vgname"}->{PPSIZE};
		$size=$args{SIZE}/(1024.0*$ppsize);
		if($size != int($size)) {
			return(-3,"Size specified is not a multiple of the volume group PP size [$ppsize Mb]");
		}
	}

	#################################################################
	# If we do not have df_portable then we should refuse to reduce	#
	# the size of the logical volume since we can not check the 	#
	# size of the file system on it - assuming the file system	#
	# is mounted of course.						#
	#################################################################
	if($size<$r[1]) {
		my $mnt=new mnttab();
		my $info=$mnt->get_from_vglv($vgname,$lvname);
		if($info) {
			my $mntpnt=$info->{FS};
			if(!$df_portable) {
				return(-4,"File system mounted but DfPortable not available to check file system size.");
			}
			# Get the size of the file system in mb
			if($type ne "ZFSVM") {
				my $ref=dfportable($mntpnt,1024);
				if($ref->{blocks}>$size) {
					return(-4,"File system is still larger than size to reduce volume to!");
				}
			}
		}
	}

	if($type eq "LVM") {
		if($size<$r[1]) {
			$cmd="lvreduce -L ${size}k -f /dev/$vgname/$lvname 2>&1 >/dev/null";
		}
		if($size>$r[1]) {
			$cmd="lvextend -L ${size}k /dev/$vgname/$lvname 2>&1 >/dev/null";
		}
	} elsif($type eq "AIXVM") {
		if($size<$r[1]) {
			return(-4,"Logical volume shrinking is not supported for the AIX Volume Manager.");
		}
		if($size>$r[1]) {
			$cmd="extendlv $lvname ${size}k 2>&1 >/dev/null";
		}
	} elsif($type eq "ZFSVM") {
		my $mb=int($size/1024);
		$cmd="$self->{ZFSVM_PATH}/zfs set quota=${mb}m $vgname/$lvname 2>&1";
	} elsif($type eq "SOLVM") {
		if($size<$r[1]) {
			return(-4,"Logical volume shrinking is not supported for the Solaris Volume Manager.");
		}
		my $diff=$size-$r[1];
		$cmd="/usr/sbin/metattach -s $vgname $lvname ${diff}k";
	} else {
		if($size<$r[1]) {
			$cmd="vxassist -f -b -g $vgname shrinkto $lvname ${size}k 2>&1";
		}
		if($size>$r[1]) {
			$cmd="vxassist -b -g $vgname growto $lvname ${size}k 2>&1";
		}
	}
	if(defined($cmd)) {
		local $SIG{CHLD}='DEFAULT';
		$r=`$cmd`;
		if($?>>8) {
			chomp $r;
			return (-4,$r);
		}
	}
	@r=get_lvinfo($self,@_,UPTODATE=>1);
	return (1,$r[1]);
}

#########################################################################
# Activate a volume group - to make use of it - also performs an 	#
# import if necessary.							#
# On AIX this does not import volume group, just varifies it of.	#
# This behaviour will be fixed when an aix platform is available to 	#
# test on!								#
#########################################################################

# We can also pass in "type" - if available - which we want to store
# when a vg is defined if possible.

sub vg_activate {
my $self=shift;
my %args=@_;
my ($vg,$type,$force);

my $use_forknwait=0;
my $use_type;

	if(exists($args{TYPE}) && defined($args{TYPE})) {
		$use_type=$args{TYPE};
	}
	if(defined(&::FORKNWAIT)) {
		$use_forknwait=1;
	}

	$force=0;
	if(exists($args{FORCE}) && $args{FORCE}==1) {
		$force=1;
	}
	if(!exists($args{VGNAME})) {
		return (-1,"Missing mandatory argument");
	}
	my @AT=();
	$vg=$args{VGNAME};
	if(defined($use_type)) {
		$type=$use_type;
	} else {
		if($self->{HAS_ZFSVM}) {
			push @AT,"ZFSVM";
		}
		if($self->{HAS_SOLVM}) {
			push @AT,"SOLVM";
		}
		if($self->{HAS_AIXVM}) {
			push @AT,"AIXVM";
		}
		if($self->{HAS_VXVM}) {
			push @AT,"VXVM";
		}
		if($self->{HAS_LVM}) {
			push @AT,"LVM";
		}
	}

	#########################################################################
	# Of course if the disk group is deported/exported type will not	#
	# be set here, and so a little more work needs to be done.		#
	# We assume that if VXVM is available it will be used
	#########################################################################
	my $rc=0;
	my $r;
	my $imported=0;
	for $type (@AT) {
		local $SIG{CHLD}='DEFAULT';

		if($type eq "LVM") {
			#########################################################
			# Check if already available and if so do nothing.	#
			#########################################################
			if(exists($self->{"LVM_LVLIST_$vg"})) {
				return(0,undef);
			}

			my $cmd="";
			if($self->{OS} eq "Linux") {
				$r=`$self->{LVM_PATH}/vgscan >/dev/null 2>&1;`;
				$r=`$self->{LVM_PATH}/vgimport $vg 2>&1`;
				$r=`$self->{LVM_PATH}/vgchange -a y $vg 2>&1`;
				$cmd="$self->{LVM_PATH}/vgimport $vg; $self->{LVM_PATH}/vgchange -a y $vg";
			} else {
				$r=`$self->{LVM_PATH}/vgchange -a y $vg 2>&1`;
				$cmd="$self->{LVM_PATH}/vgchange -a y $vg";
			}
			$rc=$?;
			if($rc==-1) {
				error("Failed to execute program: $cmd");
				last;
			}
			if($?>>8==0) {
				$imported=1;
				last;
			}
			$rc=$?>>8;
			chomp $r;
		} elsif($type eq "ZFSVM") {
			my $cmd="$self->{ZFSVM_PATH}/zpool import $vg >/dev/null 2>&1;";
			$r=`$cmd`;
			$rc=$?>>8;
			chomp $r;
			if($rc==-1) {
				error("Failed to execute program: $cmd");
				last;
			}
			if($?>>8==0) {
				$imported=1;
				last;
			}
			$rc=$?>>8;
			chomp $r;
		} elsif($type eq "AIXVM") {
			my $cmd="";
			if($force) {
				$r=`$self->{AIXVM_PATH}/varyonvg -b -f $vg >/dev/null 2>&1;`;
			} else {
				$r=`$self->{AIXVM_PATH}/varyonvg $vg >/dev/null 2>&1;`;
			}
			$rc=$?>>8;
			chomp $r;
			if($rc==-1) {
				error("Failed to execute program: $cmd");
				last;
			}
			if($?>>8==0) {
				$imported=1;
				last;
			}
			$rc=$?>>8;
			chomp $r;
		} elsif($type eq "SOLVM") {
			#########################################################
			# Check if already available and if so do nothing.	#
			#########################################################
			if(exists($self->{"SOL_LVLIST_$vg"})) {
				return(0,undef);
			}

			#########################################################
			# If we are forcing we try with -f otherwise without it.#
			#########################################################
			my $cmd;
			if($force) {
				$r=`/usr/sbin/metaset -s $vg -t -f 2>&1`;
			} else {
				$r=`/usr/sbin/metaset -s $vg -t 2>&1`;
			}
			$rc=$?;
			if($rc==-1) {
				error("Failed to execute program: $cmd");
				last;
			}
			if($?>>8==0) {
				$imported=1;
				last;
			}
			$rc=$?>>8;
			chomp $r;
		} else {
			#########################################################
			# Check if already available and if so do nothing.	#
			#########################################################
			if(exists($self->{"VXVM_LVLIST_$vg"})) {
				return(0,undef);
			}

			#########################################################
			# If we are forcing we try with -C otherwise without it.#
			#########################################################
			my $cmd=undef;
			my $cmd2=undef;
			if($force) {
				$r=`$self->{VXVM_PATH}/vxdg -tC import $vg 2>&1`;
				$cmd="$self->{VXVM_PATH}/vxdg -tC import $vg";
				$cmd2="$self->{VXVM_PATH}/vxvol -g $vg startall 2>&1";
			} else {
				$r=`$self->{VXVM_PATH}/vxdg -t import $vg 2>&1`;
				$cmd="$self->{VXVM_PATH}/vxdg -t import $vg";
				$cmd2="$self->{VXVM_PATH}/vxvol -g $vg startall 2>&1";
			}
			$rc=$?;
			if($rc==-1) {
				error("Failed to execute program: $cmd");
				last;
			}
			if($?>>8==0) {
				if(defined($cmd2)) {
					$r=`$cmd2`;
					$rc=$?;
					if($rc==-1) {
						error("Failed to execute program: $cmd2");
						last;
					}
				}
				if($?>>8!=0) {
					chomp $r;
					next;
				}
				$imported=1;
				last;
			}
			$rc=$?>>8;
			chomp $r;
		}
	}
	if(!$imported) {
		msg("Import/Varyon of '$vg' was not successful [rc=$rc]:");
		msg($r);
		return (1,$r);
	}
	_load_lvm_details($self) if $self->{HAS_LVM};
	_load_vxvm_details($self) if $self->{HAS_VXVM};
	_load_solvm_details($self) if $self->{HAS_SOLVM};
	_load_aixvm_details($self) if $self->{HAS_AIXVM};
	_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	return (0,undef);
}

#########################################################################
# When stopping use of a disk group as part of a clustered environment	#
# this routine should be called to ensure the disk group is not		#
# available by default - and thus is available for import on 		#
# other hosts in the cluster if shared storage is in use.		#
# On AIX this does not deport/export volume group, just varifies it of.	#
# This behaviour will be fixed when an aix platform is available to 	#
# test on!								#
#########################################################################

sub vg_deactivate {
my $self=shift;
my %args=@_;
my ($vg,$type);

	local $SIG{CHLD}='DEFAULT';
	if(!exists($args{VGNAME})) {
		return (-1,"Missing mandatory argument");
	}
	# The info should already be available, so no guessing
	# necessary - just find out which is loaded.
	$vg=$args{VGNAME};
	if(exists($self->{"LVM_LVLIST_$vg"})) {
		$type="LVM";
	}
	if(exists($self->{"VXVM_LVLIST_$vg"})) {
		$type="VXVM";
	}
	if(exists($self->{"ZFSVM_LVLIST_$vg"})) {
		$type="ZFSVM";
	}
	if(exists($self->{"SOL_LVLIST_$vg"})) {
		$type="SOLVM";
	}
	if(exists($self->{"AIXVM_LVLIST_$vg"})) {
		$type="AIXVM";
	}
	return(0,undef) if !defined($type);
	my $r;
	if($type eq "LVM") {
		$r=`$self->{LVM_PATH}/vgchange -a n $vg 2>&1`;
		if($self->{OS} eq "Linux") {
			# does not support shared access by default
			# so always deport volume group when deactivating.
			$r=`$self->{LVM_PATH}/vgexport $vg 2>&1`;
		}
	} elsif($type eq "AIXVM") {
		my $cmd="";
		$r=`$self->{AIXVM_PATH}/varyoffvg $vg >/dev/null 2>&1;`;
	} elsif($type eq "ZFSVM") {
		$r=`$self->{ZFSVM_PATH}/zpool export -f $vg >/dev/null 2>&1;`;
	} elsif($type eq "SOLVM") {
		$r=`/usr/sbin/metaset -s $vg -r 2>&1`;
	} else {
		$r=`$self->{VXVM_PATH}/vxvol -g $vg stopall 2>&1`;
		if($?>>8!=0) {
			my $x=$?>>8;
			chomp $r;
			msg("stopall volumes for '$vg' failed [rc=$x]:");
			msg("$r");
			return (1,$r);
		}
		$r=`$self->{VXVM_PATH}/vxdg deport $vg 2>&1`;
	}
	if($?>>8!=0) {
		my $x=$?>>8;
		chomp $r;
		msg("Deport/Export/Varyoff for '$vg' failed [rc=$x]:");
		msg("$r");
		return (1,$r);
	}
	_load_lvm_details($self) if $self->{HAS_LVM};
	_load_vxvm_details($self) if $self->{HAS_VXVM};
	_load_solvm_details($self) if $self->{HAS_SOLVM};
	_load_aixvm_details($self) if $self->{HAS_AIXVM};
	_load_zfsvm_details($self) if $self->{HAS_ZFSVM};
	return (0,undef);
}

sub check_usable_state {
my $self=shift;
my %args=@_;

	local $SIG{CHLD}='DEFAULT';
	if(!exists($args{VGNAME}) || !exists($args{ACTION})) {
		return (-1,"Missing mandatory argument [VGNAME and/or ACTION]");
	}
	my $a=lc($args{ACTION});
	my $vg=$args{VGNAME};

	# Only do something here if the vg is available locally...
	my $type=undef;
	if(exists($self->{"LVM_LVLIST_$vg"})) {
		$type="LVM";
	}
	if(exists($self->{"VXVM_LVLIST_$vg"})) {
		$type="VXVM";
	}
	if(exists($self->{"ZFSVM_LVLIST_$vg"})) {
		$type="ZFSVM";
	}
	if(exists($self->{"SOL_LVLIST_$vg"})) {
		$type="SOLVM";
	}
	if(exists($self->{"AIXVM_LVLIST_$vg"})) {
		$type="AIXVM";
	}
	return(-1,"Volume group '$vg' is not known.") if !defined($type);
	
	# Do not do anything unless zfs currently...
	if($type ne "ZFSVM") {
		return(0,undef);
	}
	# Firstly do the setup routine
	#
	my @lvs=sort {length($b) <=> length($a);} (lv_list($self,VGNAME=>$vg));
	my @errs=();
	my @oks=();
	if($a eq "validate") {
		for my $c_lv (@lvs) {
			my $cmd="$self->{ZFSVM_PATH}/zfs get -H mountpoint,canmount $vg/$c_lv";
			my $r=`$cmd 2>&1;`;
			chomp $r;
			if($?!=0) {
				push @errs,"Error running: $cmd - $r;";
				next;
			}
			my @L=split(/\n/,$r);
			my $ok=1;
			for my $cl (@L) {
				my @L2=split(/\s+/,$cl);
				if($L2[1] eq "canmount") {
					if($L2[2] ne "noauto") {
						push @errs,"ZFS Volume '$c_lv' does not have 'canmount' set to 'noauto'.";
						$ok=0;
					}
				}
				if($L2[1] eq "mountpoint") {
					if($L2[2] ne "legacy") {
						push @errs,"ZFS Volume '$c_lv' does not have 'mountpoint' set to 'legacy'.";
						$ok=0;
					}
				}
			}
			if($ok) {
				push @oks,"Validated settings for ZFS volume '$c_lv'.";
			}

		}
		if(@errs) {
			return (-2,@errs);
		}
		return(0,@oks);
	}
	if($a eq "setup") {
		for my $c_lv (@lvs) {
			my $cmd1="$self->{ZFSVM_PATH}/zfs get -H mountpoint,canmount $vg/$c_lv";
			my $cmd2="$self->{ZFSVM_PATH}/zfs umount -f $vg/$c_lv";
			my $cmd3="$self->{ZFSVM_PATH}/zfs set mountpoint=legacy $vg/$c_lv";
			my $cmd4="$self->{ZFSVM_PATH}/zfs set canmount=noauto $vg/$c_lv";
			my $r=`$cmd1 2>&1;`;
			chomp $r;
			if($?!=0) {
				push @errs,"Error running: $cmd1 - $r";
			}
			my $set_mountpoint=0;
			my $set_canmount=0;
			my @L=split(/\n/,$r);
			for my $cl (@L) {
				my @L2=split(/\s+/,$cl);
				if($L2[1] eq "canmount") {
					if($L2[2] ne "noauto") {
						$set_canmount=1;
					}
				}
				if($L2[1] eq "mountpoint") {
					if($L2[2] ne "legacy") {
						$set_mountpoint=1;
					}
				}
			}

			# Depending on set_canmount and set_mountpoint, issue
			# the necessary commands now...
			if($set_mountpoint) {
				system("$cmd2 2>/dev/null >/dev/null");
				my $r=`$cmd3 2>&1;`;
				chomp $r;
				if($?!=0) {
					push @errs,"Error running: $cmd3 - $r";
				} else {
					push @oks,"Set attribute 'mountpoint' for volume '$c_lv' to 'legacy'.";
				}
			}	
			if($set_canmount) {
				my $r=`$cmd4 2>&1;`;
				chomp $r;
				if($?!=0) {
					push @errs,"Error running: $cmd4 - $r";
				} else {
					push @oks,"Set attribute 'canmount' for volume '$c_lv' to 'noauto'.";
				}
			}
		}
		if(@errs) {
			return (-2,@errs);
		}
		return(0,@oks);
	}
	return(-1,"Action '$a' is not supported.");
}

1;

